/**
 * @file      : main.c
 * @author    : huenrong (huenrong1028@gmail.com)
 * @date      : 2020-05-30 15:47:72
 *
 * @copyright : Copyright (c) 2020  胡恩荣
 *
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "./serial/serial.h"

#define SERIAL_NUM 2

// 串口测试数据包结构体
struct serial_test_pack
{
    uint8_t header;    // 帧头(固定为0xAA)
    uint8_t cmd;       // 命令
    uint8_t data_len;  // 数据长度
    uint8_t data[256]; // 数据内容
    uint8_t check;     // 校验(check前的所有数据)
};

/**
 * @brief  单字节异或检验
 * @param  data: 源数据
 * @param  data_len: 源数据长度
 * @return 校验值
 */
static uint8_t check_xor(uint8_t *data, uint16_t data_len)
{
    uint8_t chk = 0;

    for (uint16_t i = 0; i < data_len; i++)
    {
        chk = (chk ^ data[i]);
    }

    return chk;
}

/**
 * @brief  串口接收数据包
 * @param  read_pack: 接收到的数据包
 * @param  port: 串口号
 * @param  first_timeout: 首字节超时时间(单位: ms)
 * @param  timeout: 字节间超时时间(单位: ms)
 * @return 成功: 0
 *         失败: -1
 */
static int serial_read_pack(struct serial_test_pack *read_pack, const uint8_t port,
                            const uint32_t first_timeout, const uint32_t timeout)
{
    int ret = -1;
    uint8_t data_len = 0;         // 数据长度
    uint8_t recv_data[256] = {0}; // 接收帧数据
    uint32_t recv_data_len = 0;   // 接收帧数据长度
    uint8_t recv_check = 0;       // 接收数据中的校验值
    uint8_t calc_check = 0;       // 本地计算校验值

    // 接收帧头
    ret = serial_read_data(&recv_data[recv_data_len], port, 1, first_timeout);
    // 长度或者帧头错误, 直接返回
    if ((1 != ret) || (0xAA != recv_data[recv_data_len]))
    {
        printf("read header fail\n");

        return -1;
    }
    recv_data_len++;

    // 接收cmd
    ret = serial_read_data(&recv_data[recv_data_len], port, 1, timeout);
    // 接收失败, 直接返回
    if (1 != ret)
    {
        printf("read cmd fail\n");

        return -1;
    }
    recv_data_len++;

    // 接收数据长度
    ret = serial_read_data(&recv_data[recv_data_len], port, 1, timeout);
    // 接收失败, 直接返回
    if (1 != ret)
    {
        printf("read data len fail\n");

        return -1;
    }
    data_len = recv_data[recv_data_len];
    recv_data_len++;

    // 接收数据内容和校验
    ret = serial_read_data(&recv_data[recv_data_len], port, (data_len + 1), timeout);
    // 接收失败, 直接返回
    if ((data_len + 1) != ret)
    {
        printf("read data and check fail\n");

        return -1;
    }
    recv_data_len += (data_len + 1);

    // 判断校验值
    recv_check = recv_data[recv_data_len - 1];
    calc_check = check_xor(recv_data, (recv_data_len - 1));
    // 校验通过
    if (recv_check == calc_check)
    {
        // 依次获取各字段
        uint8_t index = 0;
        read_pack->header = recv_data[index++];
        read_pack->cmd = recv_data[index++];
        read_pack->data_len = recv_data[index++];
        memcpy(read_pack->data, &recv_data[index], read_pack->data_len);
        index += read_pack->data_len;
        read_pack->check = recv_data[index];

        return 0;
    }
    else
    {
        printf("check fail, recv_check = 0x%02X, calc_check = 0x%02X\n", recv_check, calc_check);
    }

    return -1;
}

/**
 * @brief  程序入口
 * @param  argc: 参数个数
 * @param  argv: 参数列表
 * @return 成功: 0
 *         失败: -1
 */
int main(int argc, char *argv[])
{
    int ret = -1;
    uint8_t send_data[] = {0xAA, 0x01, 0x03, 0x11, 0x22, 0x33, 0xA8};
    uint32_t send_data_len = 7;
    struct serial_test_pack read_pack = {0};

    // 打开串口
    ret = open_serial(SERIAL_NUM, 115200, 8, 'N', 1);
    if (ret < 0)
    {
        perror("open serial fail");

        return -1;
    }

    // 打印发送数据
    printf("serial write data[len = %d]: ", send_data_len);
    for (uint8_t i = 0; i < send_data_len; i++)
    {
        printf("0x%02X ", send_data[i]);
    }
    printf("\n");

    // 串口发送数据
    ret = serial_write_data(SERIAL_NUM, send_data, send_data_len);
    if (ret < 0)
    {
        printf("serial send data fail\n");

        return -1;
    }

    // 串口接收数据包
    ret = serial_read_pack(&read_pack, SERIAL_NUM, 1000, 500);
    // 接收数据成功
    if (0 == ret)
    {
        printf("serial read data success\n");
    }
    else
    {
        printf("serial read data fail\n");
    }

    // 关闭串口
    close_serial(SERIAL_NUM);

    return 0;
}
